﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Collections;

namespace TSharp.Core.Util.Security
{
    /// <summary>
    /// 对称加密通用类
    /// </summary>
    public class SymmetryUtil
    {
        /// <summary>
        /// Encrypts the specified source long data.
        /// </summary>
        /// <param name="sourceLongData">The source long data.</param>
        /// <param name="KeyArray">The key array.</param>
        /// <param name="IVArray">The IV array.</param>
        /// <returns></returns>
        public static byte[] Encrypt(byte[] sourceLongData, byte[] KeyArray, byte[] IVArray)
        {
            using (TripleDES symmetricAlgorithm = TripleDES.Create())
            {
                symmetricAlgorithm.KeySize = 192;
                symmetricAlgorithm.Mode = CipherMode.CBC;
                return Encrypt(symmetricAlgorithm, sourceLongData, KeyArray, IVArray);
            }
        }

        /// <summary>
        /// Encrypts the specified symmetric algorithm.
        /// </summary>
        /// <param name="symmetricAlgorithm">The symmetric algorithm.</param>
        /// <param name="sourceLongData">The source long data.</param>
        /// <param name="KeyArray">The key array.</param>
        /// <param name="IVArray">The IV array.</param>
        /// <returns></returns>
        public static byte[] Encrypt(SymmetricAlgorithm symmetricAlgorithm, byte[] sourceLongData, byte[] KeyArray,
                                     byte[] IVArray)
        {
            using (var mStream = new MemoryStream())
            {
                //建立一个MemoryStream，这里面存放加密后的数据流
                // 使用MemoryStream 和key、IV新建一个CryptoStream 对象
                using (ICryptoTransform encryptor = symmetricAlgorithm.CreateEncryptor(KeyArray, IVArray))
                {
                    using (var cStream = new CryptoStream(mStream, encryptor, CryptoStreamMode.Write))
                    {
                        cStream.Write(sourceLongData, 0, sourceLongData.Length);
                        //把缓冲区中的最后状态更新到MemoryStream，并清除cStream的缓存区
                        cStream.FlushFinalBlock();
                        // 把加密后的数据流转成Base64的字符串
                        byte[] data = mStream.ToArray();
                        // 关闭两个streams.
                        cStream.Close();
                        mStream.Close();
                        return data;
                    }
                }
            }
        }

        /// <summary>
        /// Decrypts the specified long data bytes encrypted.
        /// </summary>
        /// <param name="longDataBytesEncrypted">The long data bytes encrypted.</param>
        /// <param name="KeyArray">The key array.</param>
        /// <param name="IVArray">The IV array.</param>
        /// <returns></returns>
        public static byte[] Decrypt(byte[] longDataBytesEncrypted, byte[] KeyArray, byte[] IVArray)
        {
            using (TripleDES des = TripleDES.Create())
            {
                des.KeySize = 192;
                des.Mode = CipherMode.CBC;
                return Decrypt(des, longDataBytesEncrypted, KeyArray, IVArray);
            }
        }

        /// <summary>
        /// Decrypts the specified symmetric algorithm.
        /// </summary>
        /// <param name="symmetricAlgorithm">The symmetric algorithm.</param>
        /// <param name="longDataBytesEncrypted">The long data bytes encrypted.</param>
        /// <param name="KeyArray">The key array.</param>
        /// <param name="IVArray">The IV array.</param>
        /// <returns></returns>
        public static byte[] Decrypt(SymmetricAlgorithm symmetricAlgorithm, byte[] longDataBytesEncrypted,
                                     byte[] KeyArray, byte[] IVArray)
        {
            // 建立一个MemoryStream，这里面存放加密后的数据流
            using (var msDecrypt = new MemoryStream())
            {
                // 使用MemoryStream 和key、IV新建一个CryptoStream 对象
                using (ICryptoTransform cdecryptor = symmetricAlgorithm.CreateDecryptor(KeyArray, IVArray))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, cdecryptor, CryptoStreamMode.Write))
                    {
                        //using (var outStream = new MemoryStream())
                        //{
                        //    var buffer = new byte[32];
                        //    int readsize;
                        //    do
                        //    {
                        //        readsize = csDecrypt.Read(buffer, 0, buffer.Length);
                        //        outStream.Write(buffer, 0, readsize);

                        //    } while (readsize != 0);
                        //    msDecrypt.Close();
                        //    csDecrypt.Close();
                        //    return outStream.ToArray();
                        //} 

                        //写入方式解密 需要是使用  new CryptoStream(msDecrypt, cdecryptor, CryptoStreamMode.Write) 写入
                        csDecrypt.Write(longDataBytesEncrypted, 0, longDataBytesEncrypted.Length);
                        csDecrypt.FlushFinalBlock();
                        return msDecrypt.ToArray();
                    }
                }
            }
        }

        /// <summary>
        /// Encrypts the file.加密文件
        /// </summary>
        /// <param name="inName">Name of the in.</param>
        /// <param name="outName">Name of the out.</param>
        /// <param name="desKey">The DES key.</param>
        /// <param name="desIV">The DES IV.</param>
        public static void EncryptFile(String inName, String outName, byte[] desKey, byte[] desIV)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inName, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    fout.SetLength(0);
                    //Create variables to help with read and write.
                    var bin = new byte[100]; //This is intermediate storage for the encryption.
                    long rdlen = 0; //This is the total number of bytes written.
                    long totlen = fin.Length; //This is the total length of the input file.
                    int len; //This is the number of bytes to be written at a time.
                    using (DES desCSP = new DESCryptoServiceProvider())
                    {
                        using (ICryptoTransform cryptoTransform = desCSP.CreateEncryptor(desKey, desIV))
                        using (var encStream = new CryptoStream(fout, cryptoTransform, CryptoStreamMode.Write))
                        {
                            //Read from the input file, then encrypt and write to the output file.
                            while (rdlen < totlen)
                            {
                                len = fin.Read(bin, 0, 100);
                                encStream.Write(bin, 0, len);
                                rdlen = rdlen + len;
                            }
                            encStream.Close();
                            fout.Close();
                            fin.Close();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Decrypts the file.解密文件
        /// </summary>
        /// <param name="inName">Name of the in.</param>
        /// <param name="outName">Name of the out.</param>
        /// <param name="desKey">The DES key.</param>
        /// <param name="desIV">The DES IV.</param>
        public static void DecryptFile(String inName, String outName, byte[] desKey, byte[] desIV)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inName, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    fout.SetLength(0);
                    //Create variables to help with read and write.
                    var buffer = new byte[100]; //This is intermediate storage for the encryption.
                    long rdlen = 0; //This is the total number of bytes written.
                    long totlen = fin.Length; //This is the total length of the input file.
                    int len; //This is the number of bytes to be written at a time.
                    using (DES des = new DESCryptoServiceProvider())
                    using (ICryptoTransform transform = des.CreateDecryptor(desKey, desIV))
                    using (var encStream = new CryptoStream(fout, transform, CryptoStreamMode.Write))
                    {
                        //Read from the input file, then encrypt and write to the output file.
                        while (rdlen < totlen)
                        {
                            len = fin.Read(buffer, 0, 100);
                            encStream.Write(buffer, 0, len);
                            rdlen = rdlen + len;
                        }
                        encStream.Close();
                        fout.Close();
                        fin.Close();
                    }
                }
            }
        }
    }
}